﻿#region Copyright (c) 10/02/2012, Olaf Kober <amarok.projects@gmail.com>
//================================================================================
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//================================================================================
#endregion

using System;
using System.Diagnostics;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;


namespace Amarok.Presentation
{
	/// <summary>
	/// </summary>
	public static class ViewHelper
	{
		#region ++ Public Static Interface ++

		/// <summary>
		/// </summary>
		/// <param name="dispatcherObject"></param>
		/// <param name="action"></param>
		public static void Invoke(DispatcherObject dispatcherObject, Action action)
		{
			Verify.NotNull(dispatcherObject, "dispatcherObject");
			Verify.NotNull(action, "action");

			if (dispatcherObject.CheckAccess())
			{
				// call directly
				action();
			}
			else
			{
				// schedule invocation
				dispatcherObject.Dispatcher.Invoke(
					DispatcherPriority.Send,
					action);
			}
		}

		/// <summary>
		/// </summary>
		/// <param name="dispatcherObject"></param>
		/// <param name="action"></param>
		public static Task InvokeAsync(DispatcherObject dispatcherObject, Action action)
		{
			Verify.NotNull(dispatcherObject, "dispatcherObject");
			Verify.NotNull(action, "action");

			if (dispatcherObject.CheckAccess())
			{
				// call directly
				action();

				// already done
				return TaskHelper.Completed;
			}
			else
			{
				// schedule invocation
				var operation = dispatcherObject.Dispatcher.BeginInvoke(
					DispatcherPriority.Normal,
					action);

#if NET45
				// return a task representing this invocation
				return operation.Task;
#else
				// return a task representing this invocation
				var taskCompletionSource = new TaskCompletionSource();

				operation.Completed += (sender, e) =>
					{
						taskCompletionSource.SignalCompletion();
					};

				return taskCompletionSource.Task;
#endif
			}
		}


		/// <summary>
		/// </summary>
		/// <param name="element"></param>
		/// <param name="viewAction"></param>
		public static void Send(UIElement element, ViewAction viewAction)
		{
			Verify.NotNull(element, "element");
			Verify.NotNull(viewAction, "action");

			Invoke(element, () => ViewActionManager.RaiseViewActionEvent(element, viewAction));
		}

		/// <summary>
		/// </summary>
		/// <param name="element"></param>
		/// <param name="viewAction"></param>
		public static Task SendAsync(UIElement element, ViewAction viewAction)
		{
			Verify.NotNull(element, "element");
			Verify.NotNull(viewAction, "action");

			return InvokeAsync(element, () => ViewActionManager.RaiseViewActionEvent(element, viewAction));
		}

		#endregion

		#region ~~ Internal Static Interface ~~

		/// <summary>
		/// </summary>
		/// <param name="target"></param>
		/// <returns></returns>
		internal static ViewDispatcher CreateDispatcher(Object target)
		{
			var handlerInfos = ViewInspector.Inspect(
				target.GetType());

			return new ViewDispatcher(
				handlerInfos,
				target);
		}

		/// <summary>
		/// </summary>
		/// <param name="element"></param>
		/// <param name="dispatcher"></param>
		internal static void SetupViewActionEventHandler(UIElement element, ViewDispatcher dispatcher)
		{
			var eventHandler = new ViewActionRoutedEventHandler(
				(sender, e) =>
				{
					e.Handled = dispatcher.Invoke(e.ViewAction);
				});

			element.AddHandler(
				ViewActionManager.ViewActionEvent,
				eventHandler);
		}

		/// <summary>
		/// </summary>
		/// <param name="element"></param>
		/// <param name="localizeCallback"></param>
		/// <param name="loadedCallback"></param>
		internal static void SetupLoadedEventHandler(FrameworkElement element, Action localizeCallback, Action loadedCallback)
		{
			element.Loaded += (sender, e) =>
				{
					try
					{
						// call OnLocalize()
						try
						{
							localizeCallback();
						}
						catch (Exception exception)
						{
							#region (tracing)
							{
								Trace.WriteLine(String.Format(CultureInfo.InvariantCulture,
									"*** WARNING: UNHANDLED EXCEPTION in View.OnLocalize():" +
									"\r\n  ViewType: {0}" +
									"\r\n  View:     {1}" +
									"\r\n\r\n{2}",
									element.GetType(),
									element,
									Helper.FormatException(exception, CultureInfo.InvariantCulture)
								));
							}
							#endregion
						}

						// call OnLoaded()
						try
						{
							loadedCallback();
						}
						catch (Exception exception)
						{
							#region (tracing)
							{
								Trace.WriteLine(String.Format(CultureInfo.InvariantCulture,
									"*** WARNING: UNHANDLED EXCEPTION in View.OnLoaded():" +
									"\r\n  ViewType: {0}" +
									"\r\n  View:     {1}" +
									"\r\n\r\n{2}",
									element.GetType(),
									element,
									Helper.FormatException(exception, CultureInfo.InvariantCulture)
								));
							}
							#endregion
						}
					}
					catch (Exception exception)
					{
						#region (tracing)
						{
							Trace.WriteLine(String.Format(CultureInfo.InvariantCulture,
								"*** WARNING: UNHANDLED EXCEPTION in View:" +
								"\r\n  ViewType: {0}" +
								"\r\n  View:     {1}" +
								"\r\n\r\n{2}",
								element.GetType(),
								element,
								Helper.FormatException(exception, CultureInfo.InvariantCulture)
							));
						}
						#endregion
					}
				};
		}

		#endregion

	}
}
